home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Nebula 1
/
Nebula One.iso
/
Mail
/
EnhanceMail.1.3
/
Source
/
Send.m
< prev
next >
Wrap
Text File
|
1996-04-08
|
19KB
|
743 lines
/* -*-C-*-
*******************************************************************************
*
* File: Send.m
* RCS: $Header: /usr/local/lib/cvs/EnhanceMail/Send.m,v 1.1.1.20 1996/04/08 22:30:51 cedman Exp $
* Description:
* Author: Carl Edman
* Created: Fri Oct 13 11:48:05 1995
* Modified: Mon Apr 8 11:10:00 1996 (Carl Edman) cedman@capitalist.princeton.edu
* Language: C
* Package: N/A
* Status: Experimental (Do Not Distribute)
*
* (C) Copyright 1995, but otherwise this file is perfect freeware.
*
*******************************************************************************
*/
#import <ctype.h>
#import "EnhanceMail.h"
#import "Send.h"
#import "SimpleString.h"
#import "Preferences.h"
#import "XFace.h"
#import "regexp.h"
static HashTable *SignatureHash=nil;
static HashTable *QuotationHash=nil;
static HashTable *ReplyBoxHash=nil;
static HashTable *InReplyToHash=nil;
/* RFC822 Date:
^( *(Mon|Tue|Wed|Thu|Fri|Sat|Sun) *, )? *([0-9][0-9]?) +(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) +([0-9][0-9][0-9]?[0-9]?) +([0-9]?[0-9]:[0-9][0-9](:[0-9][0-9])?) +(UT|GMT|EST|EDT|CST|CDT|MST|MDT|PST|PDT|1[A-Z]|\+[0-9][0-9][0-9][0-9]|-[0-9][0-9][0-9][0-9])
*/
static id interpret_escapes(const char *c, id mes, regexp *rx)
{
id s=[[SimpleString alloc] init];
const char *end=c+strlen(c),*d;
char *m;
int n;
static regexp *daterx=0, *addrrx=0;
if (!daterx)
daterx=regcomp("^(([ \t]*(Mon|Tue|Wed|Thu|Fri|Sat|Sun)[ \t]*,[ \t])?[ \t]*([0-9][0-9]?)[ \t]+(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[ \t]+([0-9][0-9][0-9]?[0-9]?))[ \t]+([0-9]?[0-9]:[0-9][0-9](:[0-9][0-9])?)");
if (!addrrx)
addrrx=regcomp("^[ \t]*(([^ \t].*)?[^ \t])[ \t]+<.*>[ \t]*$");
while(c<end) if ((*c=='%')) switch((++c<end) ? *c++ : '%')
{
case '{':
for(d=c;(c<end) && (*c!='}');c++);
m=alloca(c-d+1);
strncpy(m,d,c-d);
m[c-d]='\0';
c++;
if (!(d=[mes headerValueForKey:m])) break;
[s appendString:d];
break;
case 'd':
if ((d=[mes headerValueForKey:"Date"])==0) break;
if (daterx && regexec(daterx,d))
[s appendString:daterx->startp[1] length:daterx->endp[1]-daterx->startp[1]];
break;
case 't':
if ((d=[mes headerValueForKey:"Date"])==0) break;
if (daterx && regexec(daterx,d))
[s appendString:daterx->startp[7] length:daterx->endp[7]-daterx->startp[7]];
break;
case 'f':
if (!(d=[mes headerValueForKey:"From"])) break;
if (addrrx && regexec(addrrx,d))
[s appendString:addrrx->startp[1] length:addrrx->endp[1]-addrrx->startp[1]];
else
[s appendString:d];
break;
case 'n':
[s appendChar:'\n'];
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
for(n=*(c-1)-'0';isdigit(*c) && (c<end);c++) n=n*10+(*c-'0');
if (n<NSUBEXP && rx && rx->startp[n] && rx->endp[n])
[s appendString:rx->startp[n] length:rx->endp[n]-rx->startp[n]];
break;
default:
[s appendChar:*(c-1)];
}
else
[s appendChar:*(c++)];
return s;
}
#ifndef KANJI
static id quote_text(const char *text,int length,id mes)
{
id s=[[SimpleString alloc] init],prefix=nil;
int linebeg=0,i;
int maxlen=[MailMessage lineLength];
const char *line,*end,*word,*tmp,*prebeg,*preend;
regexp *rx=regcomp(EnhanceQuoteRegex);
if (!rx) return s;
prebeg=preend=line=text;
end=line+length;
while(line<end)
{
if (!regexec(rx,line)) for(i=0;i<NSUBEXP;i++) rx->startp[i]=rx->endp[i]=line;
word=rx->endp[0];
if ((prefix==nil) ||
((word-line)!=(preend-prebeg)) ||
(strncmp(line,prebeg,preend-prebeg)!=0))
{
prebeg=line;
preend=word;
if (prefix!=nil) prefix=[prefix free];
prefix=interpret_escapes(EnhanceQuotePrefix, mes, rx);
if (linebeg!=[s length]) linebeg=[s appendChar:'\n'];
}
while((word<end) && ((*word==' ')||(*word=='\t'))) word++;
for(line=word;(line<end)&&(*line!='\n')&&(*line!='\r');line++);
if ((*word=='\n')||(*word=='\r'))
{
if (linebeg!=[s length]) [s appendChar:'\n'];
[s appendSimpleString:prefix];
linebeg=[s appendChar:'\n'];
}
else if ([s length]-linebeg+(line-word)+
(([s length]==linebeg) ? [prefix length] : 1)<maxlen)
{
if ([s length]==linebeg)
[s appendSimpleString:prefix];
else
[s appendChar:' '];
[s appendString:word length:(line-word)];
linebeg=[s appendChar:'\n'];
}
else while(word<line)
{
for(tmp=word;(tmp<line)&&!((*tmp==' ')||(*tmp=='\t'));tmp++);
if (!isascii(*tmp)) tmp++;
if ([s length]-linebeg+(tmp-word)+1>=maxlen)
linebeg=[s appendChar:'\n'];
if ([s length]==linebeg)
[s appendSimpleString:prefix];
else
[s appendChar:' '];
for(;word<tmp;word++) [s appendChar:*word];
for(;(word<line)&&((*word==' ')||(*word=='\t'));word++);
}
if (line<end) line++;
if ((line<end)&&((*line=='\n')||(*line=='\r'))&&(*line!=*(line-1))) line++;
}
if (linebeg!=[s length]) [s appendChar:'\n'];
if (rx) free(rx);
if (prefix!=nil) [prefix free];
return s;
}
#else /* KANJI */
static int iseow(const char *d)
{
static char *oldd;
static int gotFirstByte=0;
if (d==0)
{
gotFirstByte=0;
return 0;
}
if (isascii(*d))
{
if ((*d==' ')||(*d=='\t')) return 1;
else return 0;
}
else
{
if (gotFirstByte)
{
if (oldd != d) gotFirstByte=0;
oldd = (char *)d; return 1;
}
else
{
if (oldd != d) gotFirstByte=1;
oldd = (char *)d;
if (isascii(*(d-1))&&(*(d-1)!='\n')&&(*(d-1)!='\r')&&(*(d-1)!=' ')&&(*(d-1)!='\t'))
return 1;
else
return 0;
}
}
}
static id quote_text(const char *text,int length,id mes)
{
id s=[[SimpleString alloc] init],prefix=nil;
int linebeg=0,i;
int maxlen=[MailMessage lineLength];
const char *line,*end,*word,*tmp,*prebeg,*preend;
regexp *rx=regcomp(EnhanceQuoteRegex);
char *lasteow;
if (!rx) return s;
prebeg=preend=line=text;
end=line+length;
iseow(0); tmp=0;
while(line<end)
{
if (!regexec(rx,line)) for(i=0;i<NSUBEXP;i++) rx->startp[i]=rx->endp[i]=line;
word=rx->endp[0];
if ((prefix==nil) ||
((word-line)!=(preend-prebeg)) ||
(strncmp(line,prebeg,preend-prebeg)!=0))
{
prebeg=line;
preend=word;
if (prefix!=nil) prefix=[prefix free];
prefix=interpret_escapes(EnhanceQuotePrefix, mes, rx);
if (linebeg!=[s length]) linebeg=[s appendChar:'\n'];
}
while((word<end) && iseow(word)) word++;
for(line=word;(line<end)&&(*line!='\n')&&(*line!='\r');line++);
if ((*word=='\n')||(*word=='\r'))
{
if (linebeg!=[s length]) [s appendChar:'\n'];
[s appendSimpleString:prefix];
linebeg=[s appendChar:'\n'];
iseow(0);
}
else if ([s length]-linebeg+(line-word)+
(([s length]==linebeg) ? [prefix length] : 1)<maxlen)
{
if ([s length]==linebeg)
[s appendSimpleString:prefix];
else
if (isascii(*(word-2))) [s appendChar:' '];
[s appendString:word length:(line-word)];
linebeg=[s appendChar:'\n'];
iseow(0);
}
else {
while(word<line)
{
lasteow=(char *)tmp;
for(tmp=word;(tmp<line)&&!iseow(tmp);tmp++);
if (!isascii(*tmp)) tmp++;
if ([s length]-linebeg+(tmp-word)+1>=maxlen)
{
if (!isascii(*lasteow)) [s appendChar:*lasteow];
linebeg=[s appendChar:'\n'];
}
if ([s length]==linebeg)
[s appendSimpleString:prefix];
else
{
if (!isascii(*lasteow)&&(*(lasteow+1)!='\n')&&(*(lasteow+1)!='\r'))
[s appendString:lasteow length:word-lasteow];
else if (isascii(*lasteow)) [s appendChar:' '];
}
for(;word<tmp;word++) [s appendChar:*word];
for(;(word<line)&&iseow(word);word++);
}
if (!isascii(*tmp)&&(*(tmp+1)=='\n')) [s appendChar:*tmp];
iseow(0);
}
if (line<end) line++;
if ((line<end)&&((*line=='\n')||(*line=='\r'))&&(*line!=*(line-1))) line++;
}
if (linebeg!=[s length]) [s appendChar:'\n'];
if (rx) free(rx);
if (prefix!=nil) [prefix free];
return s;
}
#endif /* KANJI */
@implementation EnhanceSend
+ finishLoading:(struct mach_header *)header
{
[self poseAs:[self superclass]];
SignatureHash=[[HashTable alloc] initKeyDesc:"@" valueDesc:"i"];
QuotationHash=[[HashTable alloc] initKeyDesc:"@" valueDesc:"i"];
ReplyBoxHash=[[HashTable alloc] initKeyDesc:"@" valueDesc:"*"];
InReplyToHash=[[HashTable alloc] initKeyDesc:"@" valueDesc:"*"];
return self;
}
+ startUnloading
{
SignatureHash=[SignatureHash free];
QuotationHash=[QuotationHash free];
ReplyBoxHash=[ReplyBoxHash free];
InReplyToHash=[InReplyToHash free];
return self;
}
- initFrame:(const NXRect *)frameRect
{
id ret=[super initFrame:frameRect];
[SignatureHash insertKey:self value:(void *)(EnhanceInsertSignature^EnhanceControlP())];
[QuotationHash insertKey:self value:(void *)(NO)];
[ReplyBoxHash insertKey:self value:0];
[InReplyToHash insertKey:self value:0];
return ret;
}
- free
{
char *tmp;
[SignatureHash removeKey:self];
[QuotationHash removeKey:self];
tmp=[ReplyBoxHash valueForKey:self];
[ReplyBoxHash removeKey:self];
if (tmp) free(tmp);
tmp=[InReplyToHash valueForKey:self];
[InReplyToHash removeKey:self];
if (tmp) free(tmp);
return [super free];
}
- deliver:message
{
id ret, def;
char *replybox=0, *buf=0;
if (EnhanceAutoSpellCheck ^ EnhanceAlternateP())
{
id spell=[NXSpellChecker sharedInstance];
id panel=(spell!=nil) ? [spell spellingPanel] : nil;
if ((spell!=nil)
&&[spell checkSpelling:NX_CheckSpellingFromStart of:self])
{
if (panel!=nil) [panel orderFront:self];
return nil;
}
else
{
if (panel!=nil) [panel orderOut:self];
}
}
replybox=[ReplyBoxHash valueForKey:self];
def=[Defaults new];
if (EnhanceRepliesWithOriginals && replybox)
{
buf=[def outgoingMailbox];
buf=strcpy(alloca(strlen(buf)+1),buf);
[def setOutgoingMailbox:replybox];
}
ret=[super deliver:message];
if (buf) [def setOutgoingMailbox:buf];
return ret;
}
- mailMessage
{
id mes=[super mailMessage];
char *tmp;
id ximage;
char buf[1024];
sprintf(buf,"Mail 3.3 (Enhance %s)",EnhanceVersion);
[mes setHeaderKey:"X-Nextstep-Mailer" value:buf];
if (tmp=[InReplyToHash valueForKey:self])
[mes setHeaderKey:"In-Reply-To" value:tmp];
if (EnhanceInsertXFace && EnhanceXFace
&& (ximage=[[NXImage alloc] initFromFile:EnhanceXFace]))
{
tmp=[ximage xFace];
ximage=[ximage free];
if (tmp)
{
[mes setHeaderKey:"X-Face" value:tmp];
free(tmp);
}
}
if (EnhanceInsertXImageURL && EnhanceXImageURL)
{
tmp=malloc(strlen(EnhanceXImageURL)+1);
strcpy(tmp,EnhanceXImageURL);
[mes setHeaderKey:"X-Image-URL" value:tmp];
free(tmp);
}
return mes;
}
- (void)defaultsChanged
{
[super defaultsChanged];
if (!editing)
[self initQuoteSig];
}
- (void)setSendFormat:(int)arg
{
[super setSendFormat:arg];
if (!editing)
[self initQuoteSig];
}
- replyAll:sender
{
id ret=[super replyAll:sender];
[self prepareReply];
return ret;
}
- reply:sender
{
id ret=[super reply:sender];
[self prepareReply];
return ret;
}
- restoreDraftFromMessage:message
{
#if 0
if (EnhanceRestoreDeletesDraft)
{
[toc setState:'d' forMsg:number flush:YES];
}
#endif
[self nowEditing];
[text setText:""];
[self updateSpecialDelivery];
return [super restoreDraftFromMessage:message];
}
- prepareReply
{
id oreader=[self getMailbox];
id obox=oreader ? [oreader mailbox] : nil;
id orig=oreader ? [oreader mailMessage] : nil;
char *old,*new;
[QuotationHash insertKey:self value:(void *)(EnhanceQuoteReplies^EnhanceControlP())];
old=[ReplyBoxHash valueForKey:self];
new=obox ? [obox dirname] : 0;
if ((old && new && strcmp(old,new))
|| (old==0 && new!=0)
|| (new!=0 && old==0))
{
if (old) free(old);
if (new) new=strcpy(malloc(strlen(new)+1),new);
[ReplyBoxHash insertKey:self value:new];
}
old=[InReplyToHash valueForKey:self];
new=orig ? [orig headerValueForKey:"Message-Id"] : 0;
if ((old && new && strcmp(old,new))
|| (old==0 && new!=0)
|| (new!=0 && old==0))
{
if (old) free(old);
if (new) new=strcpy(malloc(strlen(new)+1),new);
[InReplyToHash insertKey:self value:new];
}
if ((orig!=nil)&&EnhanceFlagReplies)
[oreader setFlagged:YES];
#ifdef KANJI
[self logNote:"The value of editing (%d) is ignored on NS3.3J.",editing];
[self initQuoteSig];
#else /* not KANJI */
if (!editing)
[self initQuoteSig];
#endif /* kANJI */
return self;
}
- initQuoteSig
{
id oreader=[self getMailbox];
id obox=oreader ? [oreader mailbox] : nil;
id orig=oreader ? [oreader mailMessage] : nil;
id otext=oreader ? [oreader text] : nil;
BOOL quotationflag=(int)[QuotationHash valueForKey:self];
BOOL signatureflag=(int)[SignatureHash valueForKey:self];
[text setAutodisplay:NO];
[text setText:""];
if (signatureflag)
{
char path[FILENAME_MAX];
const char *home=NXHomeDirectory();
const char *box=obox ? [obox dirname] : 0;
const char *file=EnhanceSignatureFilename;
BOOL success=NO;
if (file && box && !success)
{
sprintf(path,"%s/%s.rtfd",box,file);
if (sendFormat!=0 && ([text openRTFDFrom:path]==NX_RTFDErrorNone))
success=YES;
sprintf(path,"%s/%s.rtf",box,file);
if (!success && sendFormat!=0 && ([text openRichFrom:path]!=nil))
success=YES;
sprintf(path,"%s/%s",box,file);
if (!success && ([text openFrom:path]!=nil))
success=YES;
}
if (file && home && !success)
{
sprintf(path,"%s/%s.rtfd",home,file);
if (sendFormat!=0 && ([text openRTFDFrom:path]==NX_RTFDErrorNone))
success=YES;
sprintf(path,"%s/%s.rtf",home,file);
if (!success && sendFormat!=0 && ([text openRichFrom:path]!=nil))
success=YES;
sprintf(path,"%s/%s",home,file);
if (!success && ([text openFrom:path]!=nil))
success=YES;
}
if (success)
{
[text setSel:0:0];
[text replaceSel:EnhanceSignatureSeparator];
}
}
if (quotationflag && orig)
{
id s=[[SimpleString alloc] init];
if (EnhanceQuoteIntro)
{
[s includeSimpleString:interpret_escapes(EnhanceQuoteIntro,orig,0)];
[s appendChar:'\n'];
}
if (otext)
{
char *buf,*c;
int start,len;
NXSelPt beg,end;
[otext getSel:&beg:&end];
if (beg.cp!=end.cp)
{
start=beg.cp;
len=end.cp-beg.cp;
buf=alloca(len);
[otext getSubstring:buf start:start length:len];
}
else
{
start=0;
len=[otext byteLength];
buf=alloca(len);
[otext getSubstring:buf start:start length:len];
for(c=buf;c<buf+len-1;c++) if ((c[0]=='\n') && (c[1]=='\n')) break;
while ((c<buf+len) && (*c=='\n')) c++;
len-=(c-buf);
buf=c;
}
start=[s length];
[s includeSimpleString:quote_text(buf,len,orig)];
len=[s length]-start;
[text setSel:0:0];
[text replaceSel:[s string] length:[s length]];
if ((sendFormat!=0)&&EnhanceQuoteColoring)
{
[text setSel:start:start+len];
[text setSelColor:EnhanceQuoteColor];
}
[text setSel:[s length]:[s length]];
}
s=[s free];
}
[text setSel:0:0];
if (sendFormat==0) [self makeAscii:self];
[self updateSpecialDelivery];
[text setAutodisplay:YES];
[text display];
return self;
}
- openTextStream
{
[text openTextStream];
return self;
}
- (BOOL)seekToCharacterAt:(int)offset relativeTo:(int)seekMode
{
return [text seekToCharacterAt:offset relativeTo:seekMode];
}
- (int)readCharacters:(char *)buffer count:(int)count
{
return [text readCharacters:buffer count:count];
}
- (int)currentCharacterOffset
{
return [text currentCharacterOffset];
}
- (BOOL)isAtEOTS
{
return [text isAtEOTS];
}
- closeTextStream
{
[text closeTextStream];
return self;
}
- (void)selectCharactersFrom:(int)start to:(int)end
{
[text selectCharactersFrom:start to:end];
}
- (int)selectionCharacterCount
{
return [text selectionCharacterCount];
}
- (int)readCharactersFromSelection:(char *)buffer count:(int)count
{
return [text readCharactersFromSelection:buffer count:count];
}
- (void)makeSelectionVisible
{
[text makeSelectionVisible];
}
- changeSpelling:sender
{
return ([text changeSpelling:sender]==nil) ? nil : self;
}
- (int)spellDocumentTag
{
return [text spellDocumentTag];
}
@end
@implementation MediaText(EnhanceSend)
- openFrom:(const char *)file
{
BOOL ok=YES;
NXStream *s=0;
if (ok && !(s=NXMapFile(file,NX_READONLY)))
ok=NO;
if (ok && ([self readText:s]==nil))
ok=NO;
if (s)
NXClose(s);
return ok ? self : nil;
}
- openRichFrom:(const char *)file
{
BOOL ok=YES;
NXStream *s=0;
if (ok && !(s=NXMapFile(file,NX_READONLY)))
ok=NO;
if (ok && ([self readRichText:s]==nil))
ok=NO;
if (s)
NXClose(s);
return ok ? self : nil;
}
@end